#include <Python.h>
#include <opcode.h>
#include <unordered_map>
#include <algorithm>

#include "jit_assert.hpp"
#include "hir_instructions.hpp"
#include "hir_compiler.hpp"
#include "hir_exceptions.hpp"
#include "hir_dfs.hpp"

static const char * getopcodeName(int opcode)
{
#define FOREACH_OPCODE(V)\
    V(STOP_CODE) \
    V(POP_TOP) \
    V(ROT_TWO) \
    V(ROT_THREE) \
    V(DUP_TOP) \
    V(ROT_FOUR) \
    V(NOP) \
    V(UNARY_POSITIVE) \
    V(UNARY_NEGATIVE) \
    V(UNARY_NOT) \
    V(UNARY_CONVERT) \
    V(UNARY_INVERT) \
    V(BINARY_POWER) \
    V(BINARY_MULTIPLY) \
    V(BINARY_DIVIDE) \
    V(BINARY_MODULO) \
    V(BINARY_ADD) \
    V(BINARY_SUBTRACT) \
    V(BINARY_SUBSCR) \
    V(BINARY_FLOOR_DIVIDE) \
    V(BINARY_TRUE_DIVIDE) \
    V(INPLACE_FLOOR_DIVIDE) \
    V(INPLACE_TRUE_DIVIDE) \
    V(SLICE) \
    V(STORE_SLICE) \
    V(DELETE_SLICE) \
    V(STORE_MAP) \
    V(INPLACE_ADD) \
    V(INPLACE_SUBTRACT) \
    V(INPLACE_MULTIPLY) \
    V(INPLACE_DIVIDE) \
    V(INPLACE_MODULO) \
    V(STORE_SUBSCR) \
    V(DELETE_SUBSCR) \
    V(BINARY_LSHIFT) \
    V(BINARY_RSHIFT) \
    V(BINARY_AND) \
    V(BINARY_XOR) \
    V(BINARY_OR) \
    V(INPLACE_POWER) \
    V(GET_ITER) \
    V(PRINT_EXPR) \
    V(PRINT_ITEM) \
    V(PRINT_NEWLINE) \
    V(PRINT_ITEM_TO) \
    V(PRINT_NEWLINE_TO) \
    V(INPLACE_LSHIFT) \
    V(INPLACE_RSHIFT) \
    V(INPLACE_AND) \
    V(INPLACE_XOR) \
    V(INPLACE_OR) \
    V(BREAK_LOOP) \
    V(WITH_CLEANUP) \
    V(LOAD_LOCALS) \
    V(RETURN_VALUE) \
    V(IMPORT_STAR) \
    V(EXEC_STMT) \
    V(YIELD_VALUE) \
    V(POP_BLOCK) \
    V(END_FINALLY) \
    V(BUILD_CLASS) \
    V(STORE_NAME) \
    V(DELETE_NAME) \
    V(UNPACK_SEQUENCE) \
    V(FOR_ITER) \
    V(LIST_APPEND) \
    V(STORE_ATTR) \
    V(DELETE_ATTR) \
    V(STORE_GLOBAL) \
    V(DELETE_GLOBAL) \
    V(DUP_TOPX) \
    V(LOAD_CONST) \
    V(LOAD_NAME) \
    V(BUILD_TUPLE) \
    V(BUILD_LIST) \
    V(BUILD_SET) \
    V(BUILD_MAP) \
    V(LOAD_ATTR) \
    V(COMPARE_OP) \
    V(IMPORT_NAME) \
    V(IMPORT_FROM) \
    V(JUMP_FORWARD) \
    V(JUMP_IF_FALSE_OR_POP) \
    V(JUMP_IF_TRUE_OR_POP) \
    V(JUMP_ABSOLUTE) \
    V(POP_JUMP_IF_FALSE) \
    V(POP_JUMP_IF_TRUE) \
    V(LOAD_GLOBAL) \
    V(CONTINUE_LOOP) \
    V(SETUP_LOOP) \
    V(SETUP_EXCEPT) \
    V(SETUP_FINALLY) \
    V(LOAD_FAST) \
    V(STORE_FAST) \
    V(DELETE_FAST) \
    V(RAISE_VARARGS) \
    V(CALL_FUNCTION) \
    V(MAKE_FUNCTION) \
    V(BUILD_SLICE) \
    V(MAKE_CLOSURE) \
    V(LOAD_CLOSURE) \
    V(LOAD_DEREF) \
    V(STORE_DEREF) \
    V(CALL_FUNCTION_VAR) \
    V(CALL_FUNCTION_KW) \
    V(CALL_FUNCTION_VAR_KW) \
    V(SETUP_WITH) \
    V(EXTENDED_ARG) \
    V(SET_ADD) \
    V(MAP_ADD)

#define DEFINE_CASE(x) \
    case x: \
        return #x;
    switch(opcode)
    {
        FOREACH_OPCODE(DEFINE_CASE);
    default:
        fprintf(stderr,"unknown opcode %d\n",opcode);
    }
    return "";
}

namespace hir
{
#ifndef NDEBUG
void validateBasicBlocks(Compiler::BlockList & blockList)
{
    if(blockList.size() <= 1)
    {
        // make no sense to validate <=1 lists
        return ;
    }
    auto first = blockList.begin();
    auto second = first;
    ++second;
#if defined(LOG_LEVEL) && (LOG_LEVEL <= LOG_LEVEL_VERBOSE)
    BasicBlock * bbEntry = (*first).get();
    SuccessorDFSPreorderIterator i(bbEntry),end;
    for(; i != end; ++i)
    {
        //LOG_VERBOSE("bb %d: %d %d",(*i)->id(),(*i)->bciStart(),(*i)->bciEnd());
        (*i)->print(logVerbose);
    }

#endif //defined(LOG_LEVEL) && (LOG_LEVEL <= LOG_LEVEL_VERBOSE)
    for(; second != blockList.end(); ++second,++first)
    {
        auto tmp = second;
        ++tmp;
        if(tmp != blockList.end())
        {
            // exit block do look like this
            DEBUG_ASSERT((*second)->bciEnd() != -1);
            DEBUG_ASSERT((*second)->bciEnd() != -1);
            DEBUG_ASSERT((*second)->bciStart() < (*second)->bciEnd());
            DEBUG_ASSERT((*first)->bciStart() < (*second)->bciStart());
            DEBUG_ASSERT((*first)->bciEnd() <= (*second)->bciStart());
        }
        DEBUG_ASSERT((*first)->bciStart() != -1);
        DEBUG_ASSERT((*first)->bciEnd() != -1);
        DEBUG_ASSERT((*first)->bciStart() < (*first)->bciEnd());
    }
}
#endif

Compiler::Compiler(struct CompileDesc const &desc) : desc_(desc)
    , currentBasicBlockId_(0)
{

}

#define INSTR_OFFSET()  ((int)(next_instr - first_instr))
#define NEXTOP()        (*next_instr++)
#define NEXTARG()       (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
#define PEEKARG()       ((next_instr[2]<<8) + next_instr[1])

void Compiler::compile()
{
    buildBasicBlock();
    collectBasicBlockInfo();
    //buildHir();
#ifndef NDEBUG
    validateBasicBlocks(basicBlocks());
#endif
    // const unsigned char * first_instr = desc_.codes_;
    // const unsigned char * next_instr = first_instr;
    // while(true) {
    //     unsigned char opcode = NEXTOP();
    //     int oparg = 0;

    //     if (HAS_ARG(opcode))
    //         oparg = NEXTARG();
    //     switch(opcode) {
    //     case NOP:
    //         continue;
    //     case LOAD_FAST:
    //
    //         break;
    //     }
    // }
}

void Compiler::buildPreBasicBlock()
{
    // create a entry basic block here
    BasicBlock * entryBb = basicBlockAt(0);

    const unsigned char * first_instr = desc_.codes_;
    const unsigned char * end_instr = first_instr + desc_.codeLength_;
    const unsigned char * next_instr = first_instr;
    // initially using the entryBb as currentBb
    BasicBlock * currentBb = entryBb;

    while(next_instr < end_instr)
    {
        // update currentBb if currentBb is nullptr or currentBb is end
        // it must get updated before NEXTOP
        if(!currentBb || currentBb->isEnd(INSTR_OFFSET()))
        {
            currentBb = findBasicBlockAt(INSTR_OFFSET());
            if(!currentBb)
            {
                // empty bb
                if(!currentBb)
                    LOG_WARNING("currentBb is nullptr,bci + 1 = %d",INSTR_OFFSET());
                // forward the opcode
                unsigned char opcode = NEXTOP();
                int oparg = 0;

                if (HAS_ARG(opcode))
                    oparg = NEXTARG();

                continue;
            }
        }
        else
        {
            // check any bb here,of there is a bb ,end current bb
            BasicBlock * subBb = findBasicBlockAt(INSTR_OFFSET());
            if(subBb && subBb != currentBb)
            {
                // setup the relationship
                currentBb->setEnd(INSTR_OFFSET());
                currentBb->setSuccessor(subBb);
                DEBUG_ASSERT(!subBb->isEnd(INSTR_OFFSET()));
                currentBb = subBb;
            }
        }
        unsigned lastIndex = INSTR_OFFSET();
        unsigned char opcode = NEXTOP();
        int oparg = 0;

        if (HAS_ARG(opcode))
            oparg = NEXTARG();

        RELEASE_ASSERT(currentBb->predecessor().size() != 0 || currentBb->id() == 0 || currentBb->isExceptHandler() || currentBb->isFinallyHandler()); // orphan bb should not appear! or currentBb is the entry node or except|finally handler

#if defined(LOG_LEVEL) && (LOG_LEVEL == LOG_LEVEL_VERBOSE)
        if (HAS_ARG(opcode))
        {
            LOG_VERBOSE("%d:\t%d(%s), %d",
                        lastIndex,opcode,getopcodeName(opcode), oparg);
        }
        else
        {
            LOG_VERBOSE("%d:\t%d(%s)",
                        lastIndex,opcode,getopcodeName(opcode));
        }
#endif //defined(LOG_LEVEL) && (LOG_LEVEL == LOG_LEVEL_VERBOSE)
        switch(opcode)
        {
        case SETUP_LOOP:
        case SETUP_EXCEPT:
        case SETUP_FINALLY:
        {
            currentBb->setEnd(INSTR_OFFSET());
            BasicBlock * newBb = basicBlockAt(INSTR_OFFSET());
            BasicBlock * handlerBb =  basicBlockAt(INSTR_OFFSET() + oparg);
            RELEASE_ASSERT(newBb->predecessor().size() == 0 && handlerBb->predecessor().size() == 0); // currently newBb and handlerBb's dominator must be null,and it doesn't have any predecessors
            currentBb->setSuccessor(newBb);
            currentBb = newBb;
            if(opcode == SETUP_LOOP)
            {
                newBb->setSuccessor(handlerBb);
                newBb->markLoopHeader();
            }
            else if (opcode == SETUP_EXCEPT)
            {
                handlerBb->markExceptHandler();
            }
            else
            {
                handlerBb->markFinallyHandler();
            }
        }
        break;
        case JUMP_FORWARD:
        {
            uint32_t jumpToBci = INSTR_OFFSET() + oparg;
            BasicBlock * newBb = basicBlockAt(jumpToBci);
            // setup successor relationship
            currentBb->setSuccessor(newBb);
            // end current bb
            currentBb->setEnd(INSTR_OFFSET());
            currentBb = nullptr;
        }
        break;
        case POP_JUMP_IF_FALSE:
        case POP_JUMP_IF_TRUE:
        case JUMP_IF_FALSE_OR_POP:
        case JUMP_IF_TRUE_OR_POP:
        case JUMP_ABSOLUTE:
        {
            uint32_t jumpToBci = oparg;
            // copy from JUMP_FORWARD case
            BasicBlock * newBb = basicBlockAt(jumpToBci);
            // setup successor relationship
            currentBb->setSuccessor(newBb);
            // end current bb
            currentBb->setEnd(INSTR_OFFSET());
            switch(opcode)
            {
            case POP_JUMP_IF_FALSE:
            case POP_JUMP_IF_TRUE:
            case JUMP_IF_FALSE_OR_POP:
            case JUMP_IF_TRUE_OR_POP:
            {
                BasicBlock * newBb = basicBlockAt(INSTR_OFFSET());
                currentBb->setSuccessor(newBb);
                currentBb = newBb;
            }
            break;
            default:
                currentBb = nullptr;
                break;
            }
        }
        break;
        case FOR_ITER:
        {
            uint32_t jumpToBci = INSTR_OFFSET() + oparg;
            // current bb must be a loop header
            DEBUG_ASSERT(currentBb->isLoopHeader());
            if(currentBb->isLoopHeader() == false)
            {
                throw CompilerBailoutException("current bb is not a loop header");
            }
            // currentBb is not a loop header,but this instr is
            currentBb->unmarkLoopHeader();
            // new a bb in this instr
            uint32_t currentBci = INSTR_OFFSET() - 3;
            BasicBlock * newLoopHeader = basicBlockAt(currentBci);
            // extra sanity test
            RELEASE_ASSERT(newLoopHeader->predecessor().size() == 0 && newLoopHeader->isLoopHeader() == false);

            // setup successor
            currentBb->setEnd(currentBci);
            currentBb->setSuccessor(newLoopHeader);

            // next instr will begins in loopBody
            BasicBlock * loopBody = basicBlockAt(INSTR_OFFSET());

            //setup successor
            newLoopHeader->setEnd(INSTR_OFFSET());
            newLoopHeader->setSuccessor(loopBody);

            loopBody->setSuccessor(newLoopHeader);
            // loopBody will end in jump to bci
            loopBody->setEnd(jumpToBci);
            // new bb beyone  FOR_ITER block
            BasicBlock * newBb = basicBlockAt(jumpToBci);
            // setup successor
            newLoopHeader->setSuccessor(newBb);
            // currentBb switch to loopBody
            currentBb = loopBody;
        }
        break;
        case RETURN_VALUE:
        {
            currentBb->setEnd(INSTR_OFFSET());
            // endBlock always in the last position
            BasicBlock * endBlock = basicBlockAt(-1);
            currentBb->setSuccessor(endBlock);
            currentBb = nullptr;
        }
        break;
        }
    }
}

// data structure to collect BREAK_LOOP CONTINUE_LOOP info
// input : basic block graph after jump,setup_xxx analysis
// output : an array of break & continue info(JumpInfo) with their final jump position set
struct JumpInfo
{
    uint32_t loop_interrupt_;
    uint32_t handler_;
    unsigned char opcode_;
};

class BlockStack
{
public:
    enum Type
    {
        LOOP,
        EXCEPT,
        FINALLY
    };
    struct BlockInfo
    {
        Type type_;
        uint32_t handler_;
    };
    typedef std::vector<BlockInfo> BlockInfoStack;
public:
    inline void pushBlockInfo(Type type,uint32_t handler)
    {
        stack_.push_back({type,handler});
    }
    inline void popBlockInfo()
    {
        stack_.pop_back();
    }
    inline const BlockInfo & currentLoopInfo()
    {
        return stack_.back();
    }
    BlockInfo * findLastLoopInfo()
    {
        for(auto i = stack_.rbegin();
                i != stack_.rend();
                ++i)
        {
            if(i->type_ == LOOP)
            {
                return &(*i);
            }
        }
        return nullptr;
    }

private:
    BlockInfoStack stack_;
};


void Compiler::fixLoopJumps()
{
    typedef std::vector<JumpInfo> JumpVector;
    JumpVector jumps;
    typedef DFSPreorderIterator<BlockStack,SuccessorFunctor> dfsiterator;
    dfsiterator i(basicBlocks().begin()->get()),end;
    for(; i != end; ++i)
    {
        BasicBlock * currentBb = *i;
        BlockStack & data = i.data();
        const unsigned char * first_instr = desc_.codes_;
        const unsigned char * end_instr = desc_.codes_ + currentBb->bciEnd();
        const unsigned char * next_instr = desc_.codes_+ currentBb->bciStart();
        while(next_instr < end_instr)
        {
            unsigned lastIndex = INSTR_OFFSET();

            unsigned char opcode = NEXTOP();
            int oparg = 0;

            if (HAS_ARG(opcode))
                oparg = NEXTARG();
            switch(opcode)
            {
            case SETUP_LOOP:
                data.pushBlockInfo(BlockStack::LOOP,oparg + INSTR_OFFSET());
                break;
            case SETUP_EXCEPT:
                data.pushBlockInfo(BlockStack::EXCEPT,oparg + INSTR_OFFSET());
                break;
            case SETUP_FINALLY:
                data.pushBlockInfo(BlockStack::FINALLY,oparg + INSTR_OFFSET());
                break;
            case POP_BLOCK:
                data.popBlockInfo();
                break;
            case CONTINUE_LOOP:
            case BREAK_LOOP:
            {
                // first looking for the loop in the stack
                BlockStack::BlockInfo * last = data.findLastLoopInfo();
                DEBUG_ASSERT(last != nullptr && "debug info should never be null");
                if(!last)
                {
                    throw CompilerBailoutException("debug info should never be null");
                }
                if(BREAK_LOOP == opcode)
                {
                    jumps.push_back({lastIndex,last->handler_,opcode});
                }
                else
                {
                    // CONTINUE_LOOP
                    jumps.push_back({lastIndex,static_cast<uint32_t>(oparg),opcode});
                }
            }
            break;

            }
        }
    }
    // fix jumps
    for(auto & j : jumps)
    {
        BasicBlock * b = findBasicBlockContaining(j.loop_interrupt_);
        DEBUG_ASSERT(b != nullptr  && "jump bci lays beyone all basic blocks");
        if(b == nullptr)
        {
            throw CompilerBailoutException("jump bci lays beyone all basic blocks");
        }
        // break bbs
        int bci_offset = 0;
        switch(j.opcode_)
        {
        case BREAK_LOOP:
            bci_offset = 1;
            break;
        case CONTINUE_LOOP:
            bci_offset = 3;
            break;
        default:
            DEBUG_ASSERT(false && "unknown opcode for jumps");
            break;
        }
        // try to find bb containing interrupt inst first
        BasicBlock * containing = findBasicBlockContaining(j.loop_interrupt_);
        DEBUG_ASSERT(containing != nullptr && "containing should never be null");
        // first set new end
        containing->setEnd(j.loop_interrupt_ + bci_offset);
        // free all successor first
        // handle manually and carefully here instead using for(auto x:xxx)
        {
            for(auto i = containing->successor().begin(); i != containing->successor().end();/*manually handle*/)
            {
                BasicBlock * successor = *i;
                ++i; //that manually handle
                successor->freePredecessor(containing);
            }
        }
        // setup successor relationship for
        BasicBlock * newSuccessor = findBasicBlockAt(j.handler_);
        RELEASE_ASSERT(newSuccessor != nullptr && "newSuccessor always not null");
        containing->setSuccessor(newSuccessor);
        // sanity check
        if(j.opcode_ == CONTINUE_LOOP)
        {
            DEBUG_ASSERT(newSuccessor->isLoopHeader() && " newSuccessor must be a loop header");
        }

    }
}

void Compiler::buildBasicBlock()
{
    this->buildPreBasicBlock();
    this->fixLoopJumps();
}

template <class T,class Y>
void Compiler::buildPhi(T & map,Y & storeBits)
{
    LocalDesc::LocalBitmap mergedInput;
    // build every store to local a phi
    for(auto &i:basicBlocks())
    {
        BasicBlock * parent = i.get();
        for(auto &j:parent->successor())
        {

            BasicBlock *& currentBb = j;
            if(currentBb->isEndBlock())
            {
                continue;
            }
            // build the phi for currentBb here
            // FIXME:build a vector for cache
            for(int k = 0; k < LocalDesc::MAX_LOCAL_COUNT; ++k)
            {
                if(storeBits.test(k))
                {
                    if(map[parent->id()].isOutputSet(k))
                    {
                        currentBb->addPhiFor(k,map[parent->id()].getLastStoreLocation(k));
                    }
                    else
                    {
                        // using parent's phi
                        currentBb->addPhiFromPhi(k,parent->id());
                    }
                }
            }
        }
    }
}

void Compiler::collectBasicBlockInfo()
{
    // FIXME:just collect load_fast store fast now!
    typedef std::unordered_map<uint32_t,LocalDesc> Map;
    Map localDescMap;
    LocalDesc::LocalBitmap storeBits;
    for(auto &i:basicBlocks())
    {
        BasicBlock * currentBb = i.get();
        const unsigned char * first_instr = desc_.codes_;
        const unsigned char * end_instr = desc_.codes_ + currentBb->bciEnd();
        const unsigned char * next_instr = desc_.codes_+ currentBb->bciStart();
        LocalDesc & desc = localDescMap[currentBb->id()];
        while(next_instr < end_instr)
        {
            unsigned lastIndex = INSTR_OFFSET();

            unsigned char opcode = NEXTOP();
            int oparg = 0;

            if (HAS_ARG(opcode))
                oparg = NEXTARG();
            switch(opcode)
            {
            case LOAD_FAST:
                break; // no need
            case STORE_FAST:
                DEBUG_ASSERT(oparg < LocalDesc::MAX_LOCAL_COUNT);
                if(oparg >= LocalDesc::MAX_LOCAL_COUNT)
                {
                    throw CompilerBailoutException("too many locals to store");
                }
                desc.recordLocalStore(oparg,lastIndex);
                storeBits.set(oparg);
                break;

                // FIXME:TO BE DONE
                // these opcodes are more difficult to handle,
                // because side effect (e.g,calling a function) will change values
            case LOAD_ATTR:
            case LOAD_GLOBAL:
            case STORE_ATTR:
            case STORE_GLOBAL:
            default:
                break;
            }
        }
    }
    buildPhi(localDescMap,storeBits);
}

struct GraphBuildContext : public GraphBuilder
{
    typedef std::vector<uint32_t> ValueStack;
    typedef std::vector<uint32_t> BlockStack;
    typedef std::unordered_map<uint32_t/*index*/,uint32_t/*graphid*/> LocalMap;
    // the value stack
    ValueStack valueStack_;
    LocalMap localMap_;
    BlockStack blockStack_;

    explicit GraphBuildContext(InstrStorage* storage) : GraphBuilder(storage) {}
    explicit GraphBuildContext() : GraphBuilder(nullptr) {}

    void storeLocal(uint32_t index,uint32_t graphid)
    {
        localMap_[index] = graphid;
    }

    uint32_t localAt(uint32_t index)
    {
        if(localMap_.find(index) != localMap_.end())
        {
            return localMap_.at(index);
        }
        return -1;
    }

    void deleteLocal(uint32_t index)
    {
        auto found = localMap_.find(index);
        if(found != localMap_.end())
        {
            localMap_.erase(found);
        }
    }

};


static void buildPhiPlaceHolder(GraphBuildContext & buildContext,BasicBlock * bb)
{
    BasicBlock * currentBb = bb;
    BasicBlock::PhiDescList  & descList = currentBb->phiDescList();

    for(auto&i:descList)
    {
        uint32_t phi = buildContext.addPhi(i.index_);
        buildContext.storeLocal(i.index_,phi);
    }
}

void Compiler::buildHir()
{
    typedef std::unordered_map<uint32_t/*index*/,uint32_t/*graphid*/> ConstMap;
    uint32_t relativeGlobalGraphCount;
    // temporarily const map
    ConstMap const_map;
    // represent the None
    uint32_t none_const = -1;
    // build const hir
    {
        uint32_t constLen = PySequence_Size(desc_.consts_);
        // where the const instructions reside
        GraphBuilder builder(&storage_);

        for(uint32_t i = 0; i < constLen; ++i)
        {
            PyObject* o = PySequence_GetItem(desc_.consts_,i);
            DEBUG_ASSERT(o != nullptr);
            if(o == nullptr)
            {
                throw CompilerBailoutException("const object o is nullptr");
            }
            uint32_t instr = builder.addConst(i,o);
            const_map[i] = instr;
            if(o == Py_None)
            {
                // a none object
                none_const = instr;
            }
        }
        // add none const
        if(none_const == -1)
        {
            none_const = builder.addConst(-1,Py_None);
        }
    }
    // build the hir on dfs order
    typedef DFSPreorderIterator<GraphBuildContext,SuccessorFunctor> DFSIterator;
    DFSIterator i(basicBlocks().begin()->get(),GraphBuildContext(&storage_)),end;
    for(; i != end; ++i)
    {
        GraphBuildContext & buildContext = i.data();
        BasicBlock * currentBb = *i;
        const unsigned char * first_instr = desc_.codes_;
        const unsigned char * end_instr = desc_.codes_ + currentBb->bciEnd();
        const unsigned char * next_instr = desc_.codes_+ currentBb->bciStart();
        // build the phi instruction here
        buildPhiPlaceHolder(buildContext,currentBb);
        GraphBuildContext::ValueStack & valueStack = buildContext.valueStack_;
        while(next_instr < end_instr)
        {
            unsigned lastIndex = INSTR_OFFSET();

            unsigned char opcode = NEXTOP();
            int oparg = 0;

            if (HAS_ARG(opcode))
                oparg = NEXTARG();
            switch(opcode)
            {
            case LOAD_FAST:
            {
                uint32_t instr = buildContext.localAt(oparg);
                if(instr == -1)
                {
                    instr = buildContext.loadConst(oparg,lastIndex);
                }
                valueStack.push_back(instr);
            }
            break;
            case LOAD_CONST:
            {
                DEBUG_ASSERT(const_map.find(oparg) != const_map.end());
                valueStack.push_back(const_map.at(oparg));
            }
            break;
            case STORE_FAST:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                buildContext.storeLocal(oparg,valueStack.back());
                valueStack.pop_back();
            }
            break;
            case POP_TOP:
                DEBUG_ASSERT(valueStack.size() > 0);
                valueStack.pop_back();
                break;
            case ROT_TWO:
                DEBUG_ASSERT(valueStack.size() >= 2);
                std::iter_swap(valueStack.rbegin(),valueStack.rbegin() + 1);
                break;
            case ROT_THREE:
                DEBUG_ASSERT(valueStack.size() >= 3);
                std::iter_swap(valueStack.rbegin(),valueStack.rbegin() + 2);
                break;
            case ROT_FOUR:
                DEBUG_ASSERT(valueStack.size() >= 4);
                std::iter_swap(valueStack.rbegin(),valueStack.rbegin() + 3);
                break;
            case DUP_TOP:
                valueStack.push_back(valueStack.back());
                break;
            case DUP_TOPX:
                if(oparg == 2)
                {
                    uint32_t value1,value2;
                    std::size_t stackLen = valueStack.size();
                    DEBUG_ASSERT(valueStack.size() >= 2);
                    value1 = valueStack.at(stackLen - 1);
                    value2 = valueStack.at(stackLen - 2);
                    valueStack.push_back(value2);
                    valueStack.push_back(value1);
                }
                else if(oparg == 3)
                {
                    uint32_t value1,value2,value3;
                    std::size_t stackLen = valueStack.size();
                    DEBUG_ASSERT(valueStack.size() >= 3);
                    value1 = valueStack.at(stackLen - 1);
                    value2 = valueStack.at(stackLen - 2);
                    value3 = valueStack.at(stackLen - 3);
                    valueStack.push_back(value3);
                    valueStack.push_back(value2);
                    valueStack.push_back(value1);

                }
                else
                {
                    DEBUG_ASSERT(false && "oparg is not bigger than 3");
                    throw CompilerBailoutException("oparg is not bigger than 3");
                }
                break;
            case UNARY_POSITIVE:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t instr = buildContext.unaryPositive(valueStack.back(),lastIndex);
                valueStack.back() = instr;
            }
            break;
            case UNARY_NEGATIVE:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t instr = buildContext.unaryNegative(valueStack.back(),lastIndex);
                valueStack.back() = instr;
            }
            break;
            case UNARY_NOT:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t instr = buildContext.unaryNot(valueStack.back(),lastIndex);
                valueStack.back() = instr;
            }
            break;
            case UNARY_CONVERT:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t instr = buildContext.unaryConvert(valueStack.back(),lastIndex);
                valueStack.back() = instr;
            }
            break;
            case UNARY_INVERT:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t instr = buildContext.unaryInvert(valueStack.back(),lastIndex);
                valueStack.back() = instr;
            }
            break;
            case BINARY_POWER:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryPower(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_MULTIPLY:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryMultiply(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_TRUE_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryTrueDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_FLOOR_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryFloorDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_MODULO:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryModulo(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_ADD:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryAdd(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_SUBTRACT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binarySubtract(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_SUBSCR:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binarySubscr(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_LSHIFT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryLshift(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_RSHIFT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryRshift(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_AND:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryAnd(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_XOR:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryXor(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case BINARY_OR:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.binaryOr(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case LIST_APPEND:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg && valueStack.size() >= 1);
                uint32_t  stackLen = valueStack.size();
                buildContext.listAppend(valueStack[stackLen - oparg],valueStack.back(),lastIndex);
                valueStack.pop_back();
            }
            break;
            case SET_ADD:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg && valueStack.size() >= 1);
                uint32_t  stackLen = valueStack.size();
                buildContext.setAdd(valueStack[stackLen - oparg],valueStack.back(),lastIndex);
                valueStack.pop_back();
            }
            break;
            case INPLACE_POWER:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplacePower(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_MULTIPLY:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceMultiply(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_TRUE_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceTrueDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_FLOOR_DIVIDE:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceFloorDivide(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_MODULO:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceModulo(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_ADD:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceAdd(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_SUBTRACT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceSubtract(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_LSHIFT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceLshift(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_RSHIFT:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceRshift(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_AND:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceAnd(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_XOR:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceXor(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case INPLACE_OR:
            {
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                uint32_t instr = buildContext.inplaceOr(valueStack[stackLen - 2],valueStack[stackLen - 1],lastIndex);
                valueStack.pop_back();
                valueStack.back() = instr;
            }
            break;
            case SLICE+0:
            case SLICE+1:
            case SLICE+2:
            case SLICE+3:
            {
                uint32_t operand1 = -1,operand2 = -1;
                if ((opcode-SLICE) & 2)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand2 = valueStack.back();
                    valueStack.pop_back();
                }

                if ((opcode-SLICE) & 1)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand1 = valueStack.back();
                    valueStack.pop_back();
                }
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t operandObject = valueStack.back();

                uint32_t instr = buildContext.slice(operandObject,operand1,operand2,lastIndex);
                valueStack.back() = instr;
            }
            break;
            case STORE_SLICE+0:
            case STORE_SLICE+1:
            case STORE_SLICE+2:
            case STORE_SLICE+3:
            {
                uint32_t operand1 = -1,operand2 = -1;
                if ((opcode-SLICE) & 2)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand2 = valueStack.back();
                    valueStack.pop_back();
                }

                if ((opcode-SLICE) & 1)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand1 = valueStack.back();
                    valueStack.pop_back();
                }
                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t operandObject = valueStack.back();
                valueStack.pop_back();
                uint32_t operandValue = valueStack.back();
                valueStack.pop_back();

                buildContext.storeSlice(operandObject,operand1,operand2,operandValue,lastIndex);
            }
            break;
            case DELETE_SLICE+0:
            case DELETE_SLICE+1:
            case DELETE_SLICE+2:
            case DELETE_SLICE+3:
            {
                uint32_t operand1 = -1,operand2 = -1;
                if ((opcode-SLICE) & 2)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand2 = valueStack.back();
                    valueStack.pop_back();
                }

                if ((opcode-SLICE) & 1)
                {
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand1 = valueStack.back();
                    valueStack.pop_back();
                }
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t operandObject = valueStack.back();
                valueStack.pop_back();

                buildContext.deleteSlice(operandObject,operand1,operand2,lastIndex);
            }
            break;
            case STORE_SUBSCR:
            {
                uint32_t operand1,operand2,operand3;

                DEBUG_ASSERT(valueStack.size() >= 3);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                operand3 = valueStack[stackLen - 3];
                valueStack.resize(stackLen - 3);
                // that 's the correct order
                buildContext.storeSubscr(operand2,operand3,operand1,lastIndex);
            }
            break;
            case DELETE_SUBSCR:
            {
                uint32_t operand1,operand2;

                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                valueStack.resize(stackLen - 2);
                buildContext.deleteSubscr(operand2,operand1,lastIndex);
            }
            break;
            case PRINT_EXPR:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.printExpr(operand1,lastIndex);
            }
            break;
            case PRINT_ITEM_TO:
            {
                uint32_t operand1,operand2;

                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                valueStack.resize(stackLen - 2);
                buildContext.printItemTo(operand2,operand1,lastIndex);
            }
            break;
            case PRINT_ITEM:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.printItem(operand1,lastIndex);
            }
            break;
            case PRINT_NEWLINE_TO:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.printNewlineTo(operand1,lastIndex);
            }
            break;
            case PRINT_NEWLINE:
            {
                buildContext.printNewline(lastIndex);
            }
            break;
            case RAISE_VARARGS:
            {
                uint32_t operand1 = -1,operand2 = -1,operand3 = -1;

                uint32_t stackLen = valueStack.size();
                switch(oparg)
                {
                case 3:
                    DEBUG_ASSERT(valueStack.size() >= 3);
                    operand3 = valueStack.back();
                    valueStack.pop_back();
                    // fall through
                case 2:
                    DEBUG_ASSERT(valueStack.size() >= 2);
                    operand2 = valueStack.back();
                    valueStack.pop_back();
                case 1:
                    DEBUG_ASSERT(valueStack.size() >= 1);
                    operand1 = valueStack.back();
                    valueStack.pop_back();
                case 0:
                    buildContext.raiseVarargs(operand3,operand2,operand1,lastIndex);
                    break;
                }
            }
            break;
            case LOAD_LOCALS:
            {
                uint32_t instr = buildContext.loadLocals(lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case RETURN_VALUE:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t operand1 = valueStack.back();
                valueStack.pop_back();
                buildContext.returnValue(operand1,lastIndex);
            }
            break;
            case YIELD_VALUE:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t operand1 = valueStack.back();
                valueStack.pop_back();
                buildContext.yieldValue(operand1,lastIndex);
            }
            break;
            case EXEC_STMT:
            {
                uint32_t operand1,operand2,operand3;

                DEBUG_ASSERT(valueStack.size() >= 3);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                operand3 = valueStack[stackLen - 3];
                valueStack.resize(stackLen - 3);
                buildContext.execStatment(operand3,operand2,operand1,lastIndex);
            }
            break;
            case POP_BLOCK:
            {
                DEBUG_ASSERT(buildContext.blockStack_.size() > 0);
                // shrink only when blockStack_.back() less than valueStack.size()
                if(buildContext.blockStack_.back() < valueStack.size())
                    valueStack.resize(buildContext.blockStack_.back());
                buildContext.blockStack_.pop_back();
            }
            break;
            // this instruction is used to re-raise exception
            case END_FINALLY:
            {
                buildContext.endFinally(lastIndex);
            }
            break;
            case BUILD_CLASS:
            {
                uint32_t operand1,operand2,operand3;

                DEBUG_ASSERT(valueStack.size() >= 3);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                operand3 = valueStack[stackLen - 3];
                valueStack.resize(stackLen - 2);
                uint32_t instr = buildContext.buildClass(operand1,operand2,operand3,lastIndex);
                valueStack.back() = instr;
            }
            break;
            case STORE_NAME:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                // FIXME : may need a Name instr to represent name
                buildContext.storeName(operand1,oparg,lastIndex);
            }
            break;
            case DELETE_NAME:
            {
                buildContext.deleteName(oparg,lastIndex);
            }
            break;
            case UNPACK_SEQUENCE:
            {
                uint32_t operand1;
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                while(oparg--)
                {
                    uint32_t instr = buildContext.unpackSequence(operand1,oparg,lastIndex);
                    valueStack.push_back(instr);
                }
            }
            break;
            case STORE_ATTR:
            {
                uint32_t operand1,operand2;

                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                valueStack.resize(stackLen - 2);
                buildContext.storeAttr(operand1,operand2,oparg,lastIndex);
            }
            break;
            case DELETE_ATTR:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.deleteAttr(operand1,oparg,lastIndex);
            }
            break;
            case STORE_GLOBAL:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.storeGlobal(operand1,oparg,lastIndex);
            }
            break;
            case DELETE_GLOBAL:
            {
                buildContext.deleteGlobal(oparg,lastIndex);
            }
            break;
            case LOAD_NAME:
            {
                uint32_t instr = buildContext.loadName(oparg,lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case LOAD_GLOBAL:
            {
                uint32_t instr = buildContext.loadGlobal(oparg,lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case DELETE_FAST:
            {
                buildContext.deleteLocal(oparg);
            }
            break;
            case LOAD_CLOSURE:
            {
                uint32_t instr = buildContext.loadClosure(oparg,lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case LOAD_DEREF:
            {
                uint32_t instr = buildContext.loadClosure(oparg,lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case STORE_DEREF:
            {
                uint32_t operand1;

                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                valueStack.resize(stackLen - 1);
                buildContext.storeDeref(operand1,oparg,lastIndex);
            }
            break;
            case BUILD_TUPLE:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg);
                uint32_t stackLen = valueStack.size();
                // that assume std::vector using a continue memory buffer
                uint32_t * operands = &valueStack[stackLen - oparg];
                uint32_t buildInstr = buildContext.buildTuple(oparg,operands,lastIndex);
                valueStack.resize(stackLen - oparg);
                valueStack.push_back(buildInstr);
            }
            break;
            case BUILD_LIST:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg);
                uint32_t stackLen = valueStack.size();
                // that assume std::vector using a continue memory buffer
                uint32_t * operands = &valueStack[stackLen - oparg];
                uint32_t buildInstr = buildContext.buildList(oparg,operands,lastIndex);
                valueStack.resize(stackLen - oparg);
                valueStack.push_back(buildInstr);
            }
            break;
            case BUILD_SET:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg);
                uint32_t stackLen = valueStack.size();
                // that assume std::vector using a continue memory buffer
                uint32_t * operands = &valueStack[stackLen - oparg];
                uint32_t buildInstr = buildContext.buildSet(oparg,operands,lastIndex);
                valueStack.resize(stackLen - oparg);
                valueStack.push_back(buildInstr);

            }
            break;
            case BUILD_MAP:
            {
                uint32_t instr = buildContext.buildMap(oparg,lastIndex);
                valueStack.push_back(instr);
            }
            break;
            case STORE_MAP:
            {
                uint32_t operand1,operand2,operand3;

                DEBUG_ASSERT(valueStack.size() >= 3);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                operand3 = valueStack[stackLen - 3];
                valueStack.resize(stackLen - 3);
                buildContext.storeMap(operand3,operand2,operand1,lastIndex);
            }
            break;
            case MAP_ADD:
            {
                DEBUG_ASSERT(valueStack.size() >= oparg);
                uint32_t operand1,operand2,operand3;
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                operand3 = valueStack[stackLen - oparg];
                valueStack.resize(stackLen - 2);
                buildContext.mapAdd(operand3,operand2,operand1,lastIndex);
            }
            break;
            case LOAD_ATTR:
            {
                DEBUG_ASSERT(valueStack.size() >= 1);
                uint32_t & objectInstr = valueStack.back();
                objectInstr = buildContext.loadAttr(objectInstr,oparg,lastIndex);
            }
            break;
            case COMPARE_OP:
            {
                uint32_t operand1,operand2;

                DEBUG_ASSERT(valueStack.size() >= 2);
                uint32_t stackLen = valueStack.size();
                operand1 = valueStack[stackLen - 1];
                operand2 = valueStack[stackLen - 2];
                valueStack.resize(stackLen - 2);
                uint32_t instr = buildContext.compareOp(operand2,operand1,oparg,lastIndex);
                valueStack.push_back(instr);

            }
            break;


            }

        }
    }
    // fill the  phi with generated hir
    for(auto &i:basicBlocks())
    {
        // fill it
    }

}

static bool compareBci(std::unique_ptr<BasicBlock> & b1,uint32_t bci)
{
    return b1->bciStart() < bci;
}

BasicBlock * Compiler::basicBlockAt(uint32_t bci)
{
    auto it = std::lower_bound(basicBlocks_.begin(),basicBlocks_.end(),bci,compareBci);

    if(it == basicBlocks_.end() || (*it)->bciStart() != bci)
    {
        // the basic block in such bci does not exist
        if(it != basicBlocks_.end())
        {
            DEBUG_ASSERT((*it)->bciStart() > bci);
            DEBUG_ASSERT(currentBasicBlockId_ < MAX_BASIC_BLOCK_ID);
        }

        if(currentBasicBlockId_ == MAX_BASIC_BLOCK_ID)
        {
            // exceeded
            throw CompilerBailoutException("Too many basic blocks");
        }

        std::unique_ptr<BasicBlock> bb(new BasicBlock(currentBasicBlockId_++,bci));
        BasicBlock * bbRet = bb.get();
        basicBlocks_.insert(it,std::move(bb));
        return bbRet;
    }
    return it->get();
}

BasicBlock * Compiler::findBasicBlockAt(uint32_t bci)
{
    auto it = std::lower_bound(basicBlocks_.begin(),basicBlocks_.end(),bci,compareBci);
    if(it == basicBlocks_.end() || (*it)->bciStart() != bci)
    {
        return nullptr;
    }
    return it->get();
}

BasicBlock * Compiler::findBasicBlockContaining(uint32_t bci)
{
    auto it = std::lower_bound(basicBlocks_.begin(),basicBlocks_.end(),bci,compareBci);
    if(it == basicBlocks_.end() || (*it)->bciEnd() <= bci)
    {
        return nullptr;
    }
    return it->get();
}

}

